﻿#include "chinese.h"
/*! ************************************************************
 *
 * \brief 提供对log4cpp的封装，简化具体应用，实现免配置
 *
 * \verbatim
 * 变更记录：
 * 2023-06-08，林胜勇:
 *   创建文件
 * \endverbatim
 ****************************************************************/
#pragma once

#include <string>

#define SPDLOG_SHORT_LEVEL_NAMES \
    { "T", "D", "I", "W", "E", "F", "O" }

#include "spdlog/spdlog.h"

#define ROOT_LOGGER_NAME "rootLogger"
#define LOG_DEFAULT_PATTERN "%^[%C-%m-%d %H:%M:%S.%e][%L]%v%$"
#define LOG_DEFAULT_MAX_FILES 7 // 默认文件备份个数

// 以下为兼容LOG4CPP等程序的<<日志方式，如：CPPC_LOG_INFO("a="<<1<<",b="<<2.3);
#define CPPC_LOG_STREAM(level, ...)    \
    {                                  \
        std::ostringstream t_out;      \
        t_out << __VA_ARGS__;          \
        cppc::log::level(t_out.str()); \
    }
#define CPPC_LOG_INFO(...) CPPC_LOG_STREAM(info, __VA_ARGS__)
#define CPPC_LOG_TRACE(...) CPPC_LOG_STREAM(trace, __VA_ARGS__)
#define CPPC_LOG_DEBUG(...) CPPC_LOG_STREAM(debug, __VA_ARGS__)
#define CPPC_LOG_WARN(...) CPPC_LOG_STREAM(warn, __VA_ARGS__)
#define CPPC_LOG_ERROR(...) CPPC_LOG_STREAM(error, __VA_ARGS__)
#define CPPC_LOG_FATAL(...) CPPC_LOG_STREAM(fatal, __VA_ARGS__)

// 第1次、每1+interval*N次调用时打印日志
// level:[FATAL，ERROR，WARN，INFO，DEBUG，TRACE]
#define LOG_EVERY_N(level, interval, formats, ...)                              \
    do {                                                                        \
        static uint ___t_logCounter = 0;                                        \
        if ((___t_logCounter++) % interval == 0) {                              \
            cppc::log::log("", cppc::log::LOG_##level, formats, ##__VA_ARGS__); \
        }                                                                       \
    } while (false)

// 在前 n 次调用时记录日志
// level:[FATAL，ERROR，WARN，INFO，DEBUG，TRACE]
#define LOG_FIRST_N(level, n, formats, ...)                                     \
    do {                                                                        \
        static uint ___t_logCounter = 0;                                        \
        if ((++___t_logCounter) <= n) {                                         \
            cppc::log::log("", cppc::log::LOG_##level, formats, ##__VA_ARGS__); \
        }                                                                       \
    } while (false)

// 在第 n 次之后（不含n）调用时记录日志
// level:[FATAL，ERROR，WARN，INFO，DEBUG，TRACE]
#define LOG_AFTER_N(level, n, formats, ...)                                     \
    do {                                                                        \
        static uint ___t_logCounter = 0;                                        \
        if ((++___t_logCounter) > n) {                                          \
            cppc::log::log("", cppc::log::LOG_##level, formats, ##__VA_ARGS__); \
        }                                                                       \
    } while (false)

// 如果condition为真时打印日志
// level:[FATAL，ERROR，WARN，INFO，DEBUG，TRACE]
#define LOG_IF(level, condition, formats, ...)                              \
    if (condition) {                                                        \
        cppc::log::log("", cppc::log::LOG_##level, formats, ##__VA_ARGS__); \
    }
// 在满足特定条件时，再满足每隔指定次数时记录日志
//  level:[FATAL，ERROR，WARN，INFO，DEBUG，TRACE]
#define LOG_IF_EVERY_N(level, condition, interval, formats, ...)                \
    do {                                                                        \
        static uint ___t_logCounter = 0;                                        \
        if (condition && (___t_logCounter++) % interval == 0) {                 \
            cppc::log::log("", cppc::log::LOG_##level, formats, ##__VA_ARGS__); \
        }                                                                       \
    } while (false)

namespace cppc { namespace log {

/// 日志级别
enum LogLevel {
    LOG_FATAL = 0,
    LOG_ERROR = 1,
    LOG_WARN = 2,
    LOG_INFO = 3,
    LOG_DEBUG = 4,
    LOG_TRACE = 5,
    LOG_ALL = 6,
    LOG_OFF
};

/// 初始化按文件大小和个数滚动日志
/// \param level 日志级别过滤，只记录小于等于此级别的日志
/// \param filePath 文件保存路径，文件完整路径
/// \param maxFileSize 单文件最大字节
/// \param maxFiles 最大文件个数
/// \param patternLayout 日志内容的输出格式
/// \param append 是否追加，false是清除现有日志
/// \param appendToConsole 是否保留输出日志到控制台
/// \return 是否初始化成功
extern bool initRootRollingFileLog(log::LogLevel level, const std::string &filePath,
                                   size_t maxFileSize = 3 * 1024 * 1024,
                                   unsigned int maxFiles = LOG_DEFAULT_MAX_FILES,
                                   const std::string &patternLayout = LOG_DEFAULT_PATTERN,
                                   bool append = true, bool appendToConsole = true);

/// 初始化按文件名的滚动日志
/// \param level 日志级别过滤，只记录小于等于此级别的日志
/// \param logName log名称，支持同时有多个日志对象，分别向不同地方写日志
/// \param filePath 文件保存路径，文件完整路径
/// \param maxFileSize 单文件最大字节
/// \param maxFiles 最大文件个数
/// \param patternLayout 日志内容的输出格式
/// \param append 是否追加，false是清除现有日志
/// \param appendToConsole 是否保留输出日志到控制台
/// \return 是否初始化成功
extern bool initRollingFileLog(log::LogLevel level, const std::string &logName,
                               const std::string &filePath, size_t maxFileSize = 3 * 1024 * 1024,
                               unsigned int maxFiles = LOG_DEFAULT_MAX_FILES,
                               const std::string &patternLayout = LOG_DEFAULT_PATTERN,
                               bool append = true, bool appendToConsole = true);

/// 关闭所有日志输出Appenders
extern void closeLog(const std::string &loggerName);

/// 关闭所有日志输出Appenders，一般不需要手动调用
extern void closeAllLog();

/// 获取指定名称的日志对象
std::shared_ptr<spdlog::logger> getLogger(const std::string &loggerName);

template <typename... Args>
inline void traceByName(const std::string &loggerName, const std::string &formats, Args... args) {
    std::shared_ptr<spdlog::logger> logger = getLogger(loggerName);
    if (logger) {
        logger->trace(formats.c_str(), std::forward<Args>(args)...);
    } else {
        fmt::print(formats.c_str(), std::forward<Args>(args)...);
        fmt::print("\r\n");
    }
}

template <typename... Args>
inline void debugByName(const std::string &loggerName, const std::string &formats, Args... args) {
    std::shared_ptr<spdlog::logger> logger = getLogger(loggerName);
    if (logger) {
        logger->debug(formats.c_str(), std::forward<Args>(args)...);
    } else {
        fmt::print(formats.c_str(), std::forward<Args>(args)...);
        fmt::print("\r\n");
    }
}
template <typename... Args>
inline void infoByName(const std::string &loggerName, const std::string &formats, Args... args) {
    std::shared_ptr<spdlog::logger> logger = getLogger(loggerName);
    if (logger) {
        logger->info(formats.c_str(), std::forward<Args>(args)...);
    } else {
        fmt::print(formats.c_str(), std::forward<Args>(args)...);
        fmt::print("\r\n");
    }
}

template <typename... Args>
inline void warnByName(const std::string &loggerName, const std::string &formats, Args... args) {
    std::shared_ptr<spdlog::logger> logger = getLogger(loggerName);
    if (logger) {
        logger->warn(formats.c_str(), std::forward<Args>(args)...);
    } else {
        fmt::print(formats.c_str(), std::forward<Args>(args)...);
        fmt::print("\r\n");
    }
}

template <typename... Args>
inline void errorByName(const std::string &loggerName, const std::string &formats, Args... args) {
    std::shared_ptr<spdlog::logger> logger = getLogger(loggerName);
    if (logger) {
        logger->error(formats.c_str(), std::forward<Args>(args)...);
    } else {
        fmt::print(formats.c_str(), std::forward<Args>(args)...);
        fmt::print("\r\n");
    }
}

template <typename... Args>
inline void fatalByName(const std::string &loggerName, const std::string &formats, Args... args) {
    std::shared_ptr<spdlog::logger> logger = getLogger(loggerName);
    if (logger) {
        logger->critical(formats.c_str(), std::forward<Args>(args)...);
    } else {
        fmt::print(formats.c_str(), std::forward<Args>(args)...);
        fmt::print("\r\n");
    }
}

// 以上是指定具体日志对象进行输出
// 下面是调用默认日志对象输出

template <typename... Args>
inline void trace(const std::string &formats, Args... args) {
    traceByName("", formats.c_str(), std::forward<Args>(args)...);
}

template <typename... Args>
inline void debug(const std::string &formats, Args... args) {
    debugByName("", formats.c_str(), std::forward<Args>(args)...);
}
template <typename... Args>
inline void info(const std::string &formats, Args... args) {
    infoByName("", formats.c_str(), std::forward<Args>(args)...);
}

template <typename... Args>
inline void warn(const std::string &formats, Args... args) {
    warnByName("", formats.c_str(), std::forward<Args>(args)...);
}

template <typename... Args>
inline void error(const std::string &formats, Args... args) {
    errorByName("", formats.c_str(), std::forward<Args>(args)...);
}

template <typename... Args>
inline void fatal(const std::string &formats, Args... args) {
    fatalByName("", formats.c_str(), std::forward<Args>(args)...);
}

/// 统一日志接口
/// \param loggerName 日志名称，为空时使用默认日志
/// \param level 日志级别
/// \param formats 格式化字符串 如："hello {}"
/// \param args 参数 如："world"
template <typename... Args>
inline void log(const std::string &loggerName, log::LogLevel level, const std::string &formats,
                Args... args) {
    switch (level) {
    case LOG_FATAL: fatalByName(loggerName, formats, std::forward<Args>(args)...); break;
    case LOG_ERROR: errorByName(loggerName, formats, std::forward<Args>(args)...); break;
    case LOG_WARN: warnByName(loggerName, formats, std::forward<Args>(args)...); break;
    case LOG_INFO: infoByName(loggerName, formats, std::forward<Args>(args)...); break;
    case LOG_DEBUG: debugByName(loggerName, formats, std::forward<Args>(args)...); break;
    case LOG_TRACE: traceByName(loggerName, formats, std::forward<Args>(args)...); break;
    }
}

/// 统一日志接口
/// \param level 日志级别
/// \param formats 格式化字符串 如："hello {}"
/// \param args 参数 如："world"
template <typename... Args>
inline void log(log::LogLevel level, const std::string &formats, Args... args) {
    log("", level, formats, std::forward<Args>(args)...);
}
}} // namespace cppc::log
